/*
*
* ████████╗░█████╗░███████╗██████╗░░█████╗░
* ╚══██╔══╝██╔══██╗██╔════╝██╔══██╗██╔══██╗
* ░░░██║░░░██║░░██║█████╗░░██████╦╝███████║
* ░░░██║░░░██║░░██║██╔══╝░░██╔══██╗██╔══██║
* ░░░██║░░░╚█████╔╝██║░░░░░██████╦╝██║░░██║
* ░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═════╝░╚═╝░░╚═╝
* 
*  :: 九方通逊-Henry ::   Copyright©2020 tofba.com  
* 
*/
package com.lmexpress.service.impl;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lmexpress.enums.ApiCredential;
import com.lmexpress.enums.ApiOrderError;
import com.lmexpress.enums.LabelFormat;
import com.lmexpress.enums.LabelSuffix;
import com.lmexpress.orm.k5.K5Order;
import com.lmexpress.orm.k5.OrderItem;
import com.lmexpress.orm.k5.OrderVolume;
import com.lmexpress.orm.k5.Recipient;
import com.lmexpress.orm.openapi.ExpressOrderApi;
import com.lmexpress.orm.openapi.OrderInvoice;
import com.lmexpress.orm.rest.ApiLabelResponse;
import com.lmexpress.orm.rest.ApiOrderResponse;
import com.lmexpress.service.ApiService;
import com.lmexpress.util.ApiUtil;
import com.lmexpress.util.HttpClientPoolUtil;
import com.lmexpress.util.HttpClientResult;
import com.lmexpress.util.PropertiesUtil;
import com.lmexpress.util.RegexUtil;

/**
 * 
 * <K5>
 * <p>
 * http://ysdyt.kingtrans.cn/nclient/Logon?action=initMenu
 * </p>
 * <p>
 * user: JFTX_SZ
 * </p>
 * <p>
 * password: jftx112233sz
 * </p>
 * 
 * @author Henry(fba02)
 * @version [版本号, 2020-12-09]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class KingtransClient implements ApiService {
    private Logger LOGGER = LoggerFactory.getLogger(KingtransClient.class);    
    private final static String UNIT = "PCS";    
    private final static String PACKAGE_TYPE = "O";    
    private final static String GOODS_TYPE = "WPX";    
    private final static String COUNTRY = "US";    
    private final static String SUCCESS = "success";
    
    @Override
    public ApiOrderResponse preProcessCreateOrder(ExpressOrderApi expressOrderApi) {
        String countryCode = expressOrderApi.getCountryCode();
        String consigneeState = expressOrderApi.getConsigneeState();
        if (StringUtils.isBlank(countryCode) || StringUtils.isNotBlank(countryCode) && !StringUtils.equalsIgnoreCase(countryCode, COUNTRY)) {
            return new ApiOrderResponse(false, null, PropertiesUtil.getProperty("k5.usps.mail.only.support.us"), false, null);
        }
        expressOrderApi.setCountryCode(expressOrderApi.getCountryCode().toUpperCase());
        if (StringUtils.isBlank(consigneeState)) {
            return new ApiOrderResponse(false, null, PropertiesUtil.getProperty("k5.usps.mail.consignee.state.required"), false, null);
        }
        String consigneeCompanyName = StringUtils.isBlank(expressOrderApi.getConsigneeCompanyName()) ? expressOrderApi.getConsigneeName() : expressOrderApi.getConsigneeCompanyName();
        expressOrderApi.setConsigneeCompanyName(consigneeCompanyName);
        if (StringUtils.isNotBlank(consigneeState)) {
            consigneeState = consigneeState.toUpperCase();
        }
        Map<String, String> allAmericaState = ApiUtil.getAllAmericaState();
        Map<String, String> allAmericaState2 = ApiUtil.getAllAmericaState2();
        if (consigneeState.length() <= 2) {
            String upperCaseState = allAmericaState.get(consigneeState.toString());
            if (StringUtils.isBlank(upperCaseState)) {
                return new ApiOrderResponse(false, null, PropertiesUtil.getProperty("k5.usps.mail.consignee.state.illegal.code"), false, null);
            } else {
                expressOrderApi.setConsigneeState(consigneeState);
            }
        } else {
            String upperCaseState = allAmericaState2.get(consigneeState.toString());
            if (StringUtils.isBlank(upperCaseState)) {
                return new ApiOrderResponse(false, null, PropertiesUtil.getProperty("k5.usps.mail.consignee.state.illegal.name"), false, null);
            } else {
                expressOrderApi.setConsigneeState(upperCaseState);
            }
        }
        return null;
    }
    
    @Override
    public String mapping(ExpressOrderApi expressOrderApi) {
        Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
        K5Order k5Order = new K5Order();
        k5Order.setCustomerNumber(expressOrderApi.getCustomerInvoiceCode().replaceAll("-", ""));
        String consigneeState = expressOrderApi.getConsigneeState();
        String laxPort = apiCredentials.get(ApiCredential.KINGTRANS_LAX_STATE.getKey());
        if (StringUtils.isBlank(laxPort)) {
            laxPort = ApiCredential.KINGTRANS_LAX_STATE.getValue();
        }
        String jfkPort = apiCredentials.get(ApiCredential.KINGTRANS_JFK_STATE.getKey());
        if (StringUtils.isBlank(jfkPort)) {
            jfkPort = ApiCredential.KINGTRANS_JFK_STATE.getValue();
        }
        String channelCode = "";
        if (StringUtils.isNotBlank(laxPort) && laxPort.indexOf(consigneeState) > -1) {
            channelCode = apiCredentials.get(ApiCredential.KINGTRANS_USPSMAIL_LAX_PRODUCTCODE.getKey());
            if (StringUtils.isBlank(channelCode)) {
                channelCode = ApiCredential.KINGTRANS_USPSMAIL_LAX_PRODUCTCODE.getValue();
            }
        } else if (StringUtils.isNotBlank(jfkPort) && jfkPort.indexOf(consigneeState) > -1) {
            channelCode = apiCredentials.get(ApiCredential.KINGTRANS_USPSMAIL_JFK_PRODUCTCODE.getKey());
            if (StringUtils.isBlank(channelCode)) {
                channelCode = ApiCredential.KINGTRANS_USPSMAIL_JFK_PRODUCTCODE.getValue();
            }
        }
        k5Order.setChannelCode(channelCode);// US_USPS_B->UPS Mail JFK US_USPS->UPS Mail LAX
        k5Order.setCountryCode(expressOrderApi.getCountryCode());
        k5Order.setTotalWeight(expressOrderApi.getWeight() == null ? null : expressOrderApi.getWeight());
        k5Order.setNumber(expressOrderApi.getOrderPiece() == null ? 1 : expressOrderApi.getOrderPiece());
        k5Order.setPackageType(PACKAGE_TYPE);
        k5Order.setGoodsType(GOODS_TYPE);// WPX:包裹,DOC文件,PAK:PAK袋
        
        Recipient recipient = new Recipient();
        recipient.setName(expressOrderApi.getConsigneeName());
        recipient.setCompany(StringUtils.isBlank(expressOrderApi.getConsigneeCompanyName()) ? null : expressOrderApi.getConsigneeCompanyName());
        recipient.setAddres1(expressOrderApi.getConsigneeAddress());
        recipient.setAddres2(StringUtils.isBlank(expressOrderApi.getConsigneeAddress2()) ? null : expressOrderApi.getConsigneeAddress2());
        
        String consigneeTelephone = expressOrderApi.getConsigneeTelephone();
        if (StringUtils.isNotBlank(consigneeTelephone) && RegexUtil.isUsPhone(consigneeTelephone)) {
            consigneeTelephone = StringUtils.isBlank(RegexUtil.regexUsPhone(consigneeTelephone)) ? RegexUtil.regexUsPhone1(consigneeTelephone) : RegexUtil.regexUsPhone(consigneeTelephone);
        }
        String consigneeMobile = expressOrderApi.getConsigneeMobile();
        if (StringUtils.isNotBlank(consigneeMobile) && RegexUtil.isUsPhone(consigneeMobile)) {
            consigneeMobile = RegexUtil.regexUsPhone(consigneeMobile);
        }
        recipient.setTel(StringUtils.isBlank(consigneeTelephone) ? null : consigneeTelephone);
        recipient.setMobile(StringUtils.isBlank(consigneeMobile) ? null : consigneeMobile);
        recipient.setProvince(expressOrderApi.getConsigneeState());
        recipient.setCity(expressOrderApi.getConsigneeCity());
        recipient.setPost(expressOrderApi.getConsigneePostCode());
        recipient.setEmail(StringUtils.isBlank(expressOrderApi.getConsigneeEmail()) ? null : expressOrderApi.getConsigneeEmail());
        k5Order.setRecipient(recipient);
        
        List<OrderItem> OrderItems = new ArrayList<OrderItem>();
        Double totalWeight = 0D;
        Double totalValue = 0D;
        String note = "";
        OrderItem orderItem = null;
        for (OrderInvoice orderInvoice : expressOrderApi.getOrderInvoices()) {
            orderItem = new OrderItem();
            orderItem.setSku(orderInvoice.getSkuCode());
            orderItem.setCnname(orderInvoice.getSkuNameCn());
            orderItem.setEnname(orderInvoice.getSkuNameEn());
            Double value = orderInvoice.getInvoiceAmount();
            if (null != value) {
                totalValue += value;
                orderItem.setPrice(value);
            }
            Double weight = orderInvoice.getInvoiceWeight();
            if (null != weight) {
                totalWeight += weight;
            }
            orderItem.setWeight(weight);
            orderItem.setNum(orderInvoice.getInvoicePcs());
            orderItem.setCustomsCode(StringUtils.isBlank(orderInvoice.getHsCode()) ? null : orderInvoice.getHsCode());
            orderItem.setMoney(StringUtils.isBlank(expressOrderApi.getCurrencyCode()) ? DEFAULT_CURRENCY : expressOrderApi.getCurrencyCode());
            orderItem.setUnit(UNIT);
            note = note + orderInvoice.getSkuCode() + "*" + orderInvoice.getInvoicePcs() + ";";
            OrderItems.add(orderItem);
        }
        if (null == k5Order.getTotalWeight() || (null != k5Order.getTotalWeight() && k5Order.getTotalWeight() < 0.0001)) {
            k5Order.setTotalWeight(totalWeight);
        }
        k5Order.setTotalValue(totalValue);
        k5Order.setOrderItems(OrderItems);
        List<OrderVolume> Volumes = new ArrayList<OrderVolume>();
        List<com.lmexpress.orm.openapi.OrderVolume> orderVolumes = expressOrderApi.getOrderVolumes();
        OrderVolume OrderVolume = null;
        if (CollectionUtils.isNotEmpty(orderVolumes)) {
            for (com.lmexpress.orm.openapi.OrderVolume orderVolume : orderVolumes) {
                OrderVolume = new OrderVolume();
                OrderVolume.setWeight(orderVolume.getWeight());
                OrderVolume.setHeight(orderVolume.getHeight());
                OrderVolume.setWidth(orderVolume.getWidth());
                OrderVolume.setLength(orderVolume.getLength());
                OrderVolume.setNumber(1);
                Volumes.add(OrderVolume);
                break;
            }
        } else {
            OrderVolume = new OrderVolume();
            OrderVolume.setWeight(k5Order.getTotalWeight());
            OrderVolume.setHeight(10D);
            OrderVolume.setWidth(10D);
            OrderVolume.setLength(10D);
            OrderVolume.setNumber(1);
            Volumes.add(OrderVolume);
        }
        k5Order.setVolumes(Volumes);
        k5Order.setNote(note);
        Map<String, Object> k5RquestMap = new LinkedHashMap<String, Object>();
        k5RquestMap.put("Verify", buildVerify(apiCredentials));
        String orderType = apiCredentials.get(ApiCredential.KINGTRANS_TOKEN.getKey());
        if (StringUtils.isAllBlank(orderType)) {
            orderType = ApiCredential.KINGTRANS_TOKEN.getValue();
        }
        k5RquestMap.put("OrderType", orderType);
        List<K5Order> OrderDatas = new ArrayList<K5Order>();
        OrderDatas.add(k5Order);
        k5RquestMap.put("OrderDatas", OrderDatas);
        return new Gson().toJson(k5RquestMap);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public ApiOrderResponse createOrder(ExpressOrderApi expressOrderApi) {
        // 前置数据校验
        ApiOrderResponse apiOrderResponse = preProcessCreateOrder(expressOrderApi);
        if (null != apiOrderResponse) {
            return apiOrderResponse;
        }
        String orderJson = mapping(expressOrderApi);
        LOGGER.info("k5.shipment.request:{}", orderJson);
        try {
            String url = ApiCredential.KINGTRANS_CREATE_ORDER.getValue();
            HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(url, null, orderJson);
            if (null == httpClientResult || (null != httpClientResult && StringUtils.isBlank(httpClientResult.getContent()))) {
                return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
            }
            String responseJson = httpClientResult.getContent();
            LOGGER.info("k5.shipment.response:{}", responseJson);
            Map<String, Object> responseMap = new Gson().fromJson(responseJson, new TypeToken<Map<String, Object>>() {
            }.getType());
            String _statusCode = (String)responseMap.get("statusCode");
            if (!Objects.equals(_statusCode, SUCCESS)) {
                return new ApiOrderResponse(false, null, (String)responseMap.get("message"), false, null);
            }
            List<Map<String, Object>> returnDatas = (List<Map<String, Object>>)responseMap.get("returnDatas");
            if (CollectionUtils.isEmpty(returnDatas)) {
                return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), false, null);
            }
            Map<String, Object> returnData = returnDatas.get(0);
            String statusCode = (String)returnData.get("statusCode");
            if (!Objects.equals(statusCode, SUCCESS)) {
                return new ApiOrderResponse(false, null, (String)returnData.get("message"), false, responseJson);
            }
            String corpBillid = (String)returnData.get("corpBillid");// K5系统内部单号->获取面单需要
            apiOrderResponse = new ApiOrderResponse();
            apiOrderResponse.setSuccess(true);
            String trackNumber = (String)returnData.get("trackNumber");
            apiOrderResponse.setTrackNumber(trackNumber);
            apiOrderResponse.setThirdPartyorderNo(corpBillid);
            apiOrderResponse.setOriginalResponse(responseJson);
            // 下载面单
            ApiLabelResponse apiLabelResponse = label(corpBillid, trackNumber, expressOrderApi.getApiCredentials());
            if (StringUtils.isNotBlank(apiLabelResponse.getLabelPath())) {
                apiOrderResponse.setLabelUrl(apiLabelResponse.getLabelPath());// 远程面单路径
            }
            if (StringUtils.isNotBlank(apiLabelResponse.getRelativePath())) {
                apiOrderResponse.setLmLabelUrl(apiLabelResponse.getRelativePath());// 本地服务器路径
            }
            return apiOrderResponse;
        } catch (Exception e) {
            LOGGER.error("k5.shipment.exception:{}", e.getMessage());
            return new ApiOrderResponse(false, null, ApiOrderError.CREATE_ORDER_EXCEPTION.getMessage(), false, e.getMessage());
        }
    }
    
    @Override
    public ApiLabelResponse label(String trackNumber, Map<String, String> configureMap) {
        return null;
    }
    
    @Override
    public ApiLabelResponse label(String referenceNo, String trackingNumber, Map<String, String> configureMap) {
        Map<String, Object> requestMap = new LinkedHashMap<String, Object>();
        requestMap.put("Verify", buildVerify(configureMap));
        Map<String, String> corpBillidMap = new LinkedHashMap<String, String>();
        corpBillidMap.put("CorpBillid", referenceNo);
        List<Map<String, String>> corpBillidDatas = new ArrayList<Map<String, String>>();
        corpBillidDatas.add(corpBillidMap);
        requestMap.put("CorpBillidDatas", corpBillidDatas);
        requestMap.put("OrderType", 2);// 1:快件订单;2:快递制单;3:仓库订单
        requestMap.put("PrintPaper", "label");
        requestMap.put("PrintContent", "1,3");// 1:地址标签;2:报关单;3:配货信息
        Gson gson = new Gson();
        String requestJson = gson.toJson(requestMap);
        LOGGER.info("k5.label.request:{}", requestJson);
        try {
            String labelUrl = ApiCredential.KINGTRANS_LABEL_URL.getValue();
            HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(labelUrl, null, requestJson);
            if (null == httpClientResult || (null != httpClientResult && StringUtils.isBlank(httpClientResult.getContent()))) {
                return new ApiLabelResponse(trackingNumber, null);
            }
            String responseJson = httpClientResult.getContent();
            LOGGER.info("k5.label.response:{}", responseJson);
            Map<String, Object> responseMap = gson.fromJson(responseJson, new TypeToken<Map<String, Object>>() {
            }.getType());
            // {\"statusCode\":\"success\",\"url\":\"http://ysdyt.kingtrans.cn/upload/eorderlabel/2020-05-26/EYT0052620143SZ/EYT0052620143SZ_0.PDF\",\"notExistsCorpbillid\":\"\",\"message\":\"\"}
            String statusCode = (String)responseMap.get("statusCode");
            if (!Objects.equals(statusCode, SUCCESS)) {
                return new ApiLabelResponse(trackingNumber, null);
            }
            String url = (String)responseMap.get("url");
            String pdfName = trackingNumber + LabelSuffix.PDF.getSuffix();
            String centrePath = ApiUtil.getCurrentDay() + File.separator;
            String relativePath = DEFAULT_LABEL_FOLDER + File.separator + centrePath;
            String folderPath = DEFAULT_LABEL_ROOT + relativePath;
            if (!new File(folderPath).exists()) {
                new File(folderPath).mkdirs();
            }
            String fileUrl = folderPath + pdfName;
            ApiLabelResponse apiLabelResponse = new ApiLabelResponse();
            try {
                FileUtils.copyURLToFile(new URL(url), new File(fileUrl));
                apiLabelResponse.setRelativePath(relativePath + pdfName);
            } catch (Exception e) {
                LOGGER.error("k5.copy.url.to.file.exception:{}", e);
            }
            apiLabelResponse.setBarCode(trackingNumber);
            apiLabelResponse.setLabelPath(url);
            apiLabelResponse.setLabelFormat(LabelFormat.PDF.getFormat());
            return apiLabelResponse;
        } catch (Exception e) {
            LOGGER.error("k5.label.exception:{}", e.getMessage());
            return new ApiLabelResponse(trackingNumber, null);
        }
    }
    
    /**
     * Auth
     * 
     * @return
     * @author Henry(fba02)
     * @version [版本号, May 26, 2020]
     * @see [类、类#方法、类#成员]
     */
    public Map<String, String> buildVerify(Map<String, String> configureMap) {
        Map<String, String> verifyMap = new LinkedHashMap<String, String>();
        verifyMap.put("Clientid", configureMap.get(ApiCredential.KINGTRANS_CLIENTID.getKey()));
        verifyMap.put("Token", configureMap.get(ApiCredential.KINGTRANS_TOKEN.getKey()));
        return verifyMap;
    }
    
    @Override
    public ApiLabelResponse labelRender(String trackNumber, ExpressOrderApi expressOrderApi) {
        return null;
    }
    
    @Override
    public void tracking(String trackNumber, Map<String, String> configureMap) {
    }
    
    @Override
    public String trackNumber(String code, Map<String, String> configureMap) {
        return null;
    }
}